package com.shuma.upms.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.shuma.upms.user.api.feign.PermissionServiceFeignClient;
import com.shuma.upms.user.config.MqConstants;
import com.shuma.upms.user.domain.dto.PasswordResetDTO;
import com.shuma.upms.user.domain.dto.UserLoginDTO;
import com.shuma.upms.user.domain.dto.UserRegisterDTO;
import com.shuma.upms.user.domain.dto.UserUpdateDTO;
import com.shuma.upms.user.domain.dto.mq.OperationLogMessage;
import com.shuma.upms.user.domain.entity.Result;
import com.shuma.upms.user.domain.entity.User;
import com.shuma.upms.user.domain.vo.LoginSuccessVO;
import com.shuma.upms.user.domain.vo.PageVO;
import com.shuma.upms.user.domain.vo.UserInfoVo;
import com.shuma.upms.user.exception.BusinessException;
import com.shuma.upms.user.mapper.UserMapper;
import com.shuma.upms.user.service.PermissionCheckService;
import com.shuma.upms.user.service.UserService;
import com.shuma.upms.user.util.JwtUtil;
import com.shuma.upms.user.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionCheckService permissionCheckService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private PermissionServiceFeignClient permissionServiceFeignClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private JwtUtil jwtUtil;

    // 用户注册
    @Override
    public void register(UserRegisterDTO registerDTO, String ip) {
        // 校验用户名唯一性
        if (userMapper.selectByUsername(registerDTO.getUsername()) != null) {
            log.warn("用户注册失败：用户名 [{}] 已存在。", registerDTO.getUsername());
            throw new BusinessException("用户名已存在");
        }
        log.info("用户名 [{}] 唯一性校验通过，开始注册...", registerDTO.getUsername());

        // 创建并保存用户实体
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        user.setEmail(registerDTO.getEmail());
        user.setPhone(registerDTO.getPhone());
        userMapper.insert(user);
        log.info("步骤 1/3：用户数据已成功插入数据库，新用户ID为 [{}]。", user.getUserId());

        // RPC调用：绑定默认角色
        try {
            permissionServiceFeignClient.bindDefaultRole(user.getUserId());
            log.info("步骤 2/3：RPC调用权限服务成功，为用户 [id={}] 绑定了默认角色。", user.getUserId());
        } catch (Exception e) {
            log.error("严重警告：步骤 2/3 RPC调用权限服务失败！用户 [id={}] 已创建但无角色，需要人工干预。", user.getUserId(), e);
        }

        // 发送MQ消息：记录注册日志
        String detail = String.format("新用户注册成功，用户名为: %s", registerDTO.getUsername());
        sendLogToMQ(user.getUserId(), "USER_REGISTER", detail, ip);
    }

    // 用户登录
    @Override
    public LoginSuccessVO login(UserLoginDTO loginDTO, String ip) {
        String username = loginDTO.getUsername();
        log.info("用户 [{}] 正在尝试登录...", username);

        // 根据用户名查询用户
        User user = userMapper.selectByUsername(username);
        if (user == null) {
            log.warn("登录失败：用户名 [{}] 不存在。", username);
            throw new BusinessException("用户名或密码错误");
        }

        // 校验密码
        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            log.warn("登录失败：用户 [id={}, name={}] 的密码不匹配。", user.getUserId(), user.getUsername());
            throw new BusinessException("用户名或密码错误");
        }

        // 登录成功，生成Token
        log.info("登录成功：用户 [id={}, name={}]。", user.getUserId(), user.getUsername());
        String token = jwtUtil.generateToken(user.getUserId(), user.getUsername());

        String detail = String.format("用户 [%s] 登录成功。", username);
        sendLogToMQ(user.getUserId(), "USER_LOGIN", detail, ip);

        return LoginSuccessVO.builder()
                .userId(user.getUserId())
                .username(user.getUsername())
                .token(token)
                .build();
    }

    /**
     * 分页查询用户列表
     * @param pageNum 当前页码
     * @param pageSize 每页数量
     * @return 分页结果VO
     */
    @Override
    public PageVO<UserInfoVo> listUsers(int pageNum, int pageSize) {
        Long operatorId = UserContextHolder.getUserId();
        String operatorRole = permissionCheckService.getOperatorRole();

        PageInfo<User> pageInfo;
        List<User> userList;

        switch (operatorRole) {
            case PermissionCheckService.ROLE_SUPER_ADMIN:
                log.info("操作者 [id={}] 是超级管理员，正在分页查询所有用户 (页码: {}, 每页: {})。", operatorId, pageNum, pageSize);
                // 对超级管理员，直接分页查询所有用户
                PageHelper.startPage(pageNum, pageSize);
                userList = userMapper.selectAll();
                pageInfo = new PageInfo<>(userList);
                break;

            case PermissionCheckService.ROLE_ADMIN:
                log.info("操作者 [id={}] 是管理员，准备分页查询所有普通用户 (页码: {}, 每页: {})。", operatorId, pageNum, pageSize);

                //  1: 获取所有用户的ID，用于后续查询角色
                List<Long> allUserIds = userMapper.selectAllUserIds(); // 假设 UserMapper 有此方法

                //  2: 在内存中筛选出所有普通用户的ID
                List<Long> normalUserIds = allUserIds.stream()
                        .filter(userId -> PermissionCheckService.ROLE_USER.equals(permissionCheckService.getUserRole(userId)))
                        .collect(Collectors.toList());

                if (normalUserIds.isEmpty()) {
                    // 如果没有普通用户，直接返回空的分页结果
                    pageInfo = new PageInfo<>(Collections.emptyList());
                } else {
                    //  3: 启动分页，并根据筛选出的ID列表去数据库查询
                    PageHelper.startPage(pageNum, pageSize);
                    userList = userMapper.selectUsersByIds(normalUserIds);
                    pageInfo = new PageInfo<>(userList);
                }
                break;

            default: // 普通用户
                log.info("操作者 [id={}] 是普通用户，仅查询自身信息。", operatorId);
                PageHelper.startPage(pageNum, pageSize);
                User self = userMapper.selectById(operatorId);
                userList = self != null ? Collections.singletonList(self) : Collections.emptyList();
                pageInfo = new PageInfo<>(userList);
                break;
        }

        // 统一处理：将 User 列表转换为 UserInfoVo 列表
        List<UserInfoVo> voList = pageInfo.getList().stream()
                .map(this::convertToUserInfoVo)
                .collect(Collectors.toList());

        // 统一处理：封装并返回自定义的 PageVO
        return new PageVO<>(voList, pageInfo.getTotal());
    }

    @Override
    public UserInfoVo getUserInfo(Long userId) {
        // 权限检查已在Controller层完成
        log.info("准备查询用户 [id={}] 的详细信息...", userId);
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("查询的用户不存在");
        }
        return convertToUserInfoVo(user);
    }

    @Override
    public void updateUserInfo(Long userId, UserUpdateDTO dto, String ip) {
        Long operatorId = UserContextHolder.getUserId();
        log.info("操作者 [id={}] 准备更新用户 [id={}] 的信息。", operatorId, userId);

        // 记录更新前的状态
        User oldUser = userMapper.selectById(userId);

        updateUserBasicInfo(userId, dto); // 更新基础信息
        updateUserRole(userId, dto.getRoleCode()); // 更新角色信息

        String detail = String.format("操作者[id=%d]更新了用户[id=%d]的信息。旧信息: {username=%s, email=%s}, 新信息: {username=%s, email=%s}",
                operatorId, userId, oldUser.getUsername(), oldUser.getEmail(), dto.getUsername(), dto.getEmail());
        sendLogToMQ(operatorId, "UPDATE_USER_INFO", detail, ip);
    }

    @Override
    public void resetPassword(Long userId, PasswordResetDTO dto, String ip) {
        // 权限检查已在Controller层完成
        String newEncryptedPassword = passwordEncoder.encode(dto.getNewPassword());
        int updatedRows = userMapper.updatePassword(userId, newEncryptedPassword);
        if (updatedRows == 0) {
            throw new BusinessException("密码重置失败，用户可能不存在");
        }
        log.info("用户 [id={}] 的密码已成功重置。", userId);

        Long operatorId = UserContextHolder.getUserId();
        String detail = String.format("操作者[id=%d]重置了用户[id=%d]的密码。", operatorId, userId);
        sendLogToMQ(operatorId, "RESET_PASSWORD", detail, ip);
    }

    /**
     * 辅助方法：将User实体转换为UserInfoVo，并附加角色信息。
     */
    private UserInfoVo convertToUserInfoVo(User user) {
        if (user == null) return null;
        UserInfoVo vo = new UserInfoVo();
        BeanUtils.copyProperties(user, vo, "password");
        vo.setRoleCode(permissionCheckService.getUserRole(user.getUserId()));
        return vo;
    }

    /**
     * 辅助方法：更新用户的基本信息（用户名、邮箱、手机）。
     */
    private void updateUserBasicInfo(Long userId, UserUpdateDTO dto) {
        boolean needsUpdate = StringUtils.hasText(dto.getUsername()) ||
                StringUtils.hasText(dto.getEmail()) ||
                StringUtils.hasText(dto.getPhone());
        if (!needsUpdate) {
            log.info("用户 [id={}] 无需更新基本信息，跳过。", userId);
            return;
        }

        if (StringUtils.hasText(dto.getUsername())) {
            User targetUser = userMapper.selectById(userId);
            if (targetUser == null) {
                throw new BusinessException("操作失败，目标用户不存在");
            }
            if (!dto.getUsername().equals(targetUser.getUsername()) && userMapper.selectByUsername(dto.getUsername()) != null) {
                throw new BusinessException("更新失败，用户名已被占用");
            }
        }

        User userToUpdate = new User();
        userToUpdate.setUserId(userId);
        userToUpdate.setUsername(dto.getUsername());
        userToUpdate.setEmail(dto.getEmail());
        userToUpdate.setPhone(dto.getPhone());
        userMapper.updateUser(userToUpdate);
        log.info("用户 [id={}] 的基本信息已成功更新。", userId);
    }

    /**
     * 辅助方法：更新用户的角色（仅限超级管理员操作）。
     */
    private void updateUserRole(Long targetUserId, String newRoleCode) {
        if (!StringUtils.hasText(newRoleCode)) {
            return; // 如果没有传递新角色，则直接跳过
        }

        permissionCheckService.checkRoleUpdatePermission(targetUserId, newRoleCode);

        log.info("权限检查通过，准备为用户 [id={}] 执行角色变更...", targetUserId);
        Result<?> rpcResult;
        if (PermissionCheckService.ROLE_ADMIN.equals(newRoleCode)) {
            rpcResult = permissionServiceFeignClient.upgradeToAdmin(targetUserId);
        } else {
            rpcResult = permissionServiceFeignClient.downgradeToUser(targetUserId);
        }

        if (rpcResult.getCode() != 0) {
            String errorMessage = "角色变更失败: " + rpcResult.getMessage();
            log.error("角色变更RPC调用失败。用户ID: {}, 错误信息: {}", targetUserId, rpcResult.getMessage());
            throw new BusinessException(errorMessage);
        }
        log.info("用户 [id={}] 的角色已成功变更为 [{}]。", targetUserId, newRoleCode);
    }

    /**
     * 发送操作日志到消息队列的通用辅助方法
     * @param userId   执行操作的用户ID
     * @param action   操作类型 (如: USER_LOGIN, UPDATE_USER_INFO)
     * @param detail   操作详情
     * @param ip       操作者IP地址
     */
    private void sendLogToMQ(Long userId, String action, String detail, String ip) {
        try {
            OperationLogMessage logMessage = new OperationLogMessage();
            logMessage.setUserId(userId);
            logMessage.setAction(action);
            logMessage.setDetail(detail);
            logMessage.setIp(ip); // IP可以为null

            rabbitTemplate.convertAndSend(MqConstants.LOG_EXCHANGE, MqConstants.LOG_ROUTING_KEY, logMessage);
            log.info("操作日志已发送至MQ：Action [{}], UserId [{}]", action, userId);
        } catch (Exception e) {
            log.error("发送操作日志到MQ时发生异常！Action: {}, UserId: {}", action, userId, e);
        }
    }
}